/**
  Szybkie operacje na listach postingowych
  wersja z rankingiem
*/
#include <stdio.h>
#include <math.h>
#undef assert
#define assert(X) {if(!(X)){printf("assertion failed on line %d\n >>%s\n", __LINE__, #X);++*((int*)(NULL));}}
#define inline 

const int inf = 100000000;
float* pageRankofArticles;
int* lenOfArticles;
int* score;
int* inne;
float c1, c2, c3, c4, c5, c6, c7, c8, c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20,c21,c22,c23,c24;
#define maxSize 1000000

/// strutkrua reprezentujaca liste postingowa dla danego slowa
struct list {
    int *arts;
    float *relevance;
    int size;
    int fromIndex;
};

typedef struct list list;


/// A, B --> C
static void _andArticles ( list *argA, list *argB, list *argC  ){
  
  int *LA = argA->arts; int lenA = argA->size;
  int *LB = argB->arts; int lenB = argB->size;
  int *LC = argC->arts;
  
  float *RA = argA->relevance;
  float *RB = argB->relevance;
  float *RC = argC->relevance;
  
  int ia = 0;
  int ib = 0;
  int ic = 0;

  while ( ia < lenA && ib < lenB ){
    if ( LA[ia] < LB[ib] ) ++ia;
    else if ( LA[ia] > LB[ib] ) ++ib;
    else {
      LC[ic] = LA[ia];
      RC[ic] = RA[ia] + RB[ib]; //< suma istotnosci
      ++ia, ++ib, ++ic;
    }
  }
  argC->size = ic;
}


/// A, B --> C
static void _orArticles ( list *argA, list *argB, list *argC  ){
  
  int *LA = argA->arts; int lenA = argA->size;
  int *LB = argB->arts; int lenB = argB->size;
  int *LC = argC->arts;
  
  float *RA = argA->relevance;
  float *RB = argB->relevance;
  float *RC = argC->relevance;
  
  int ia = 0;
  int ib = 0;
  int ic = 0;
  
  while ( ia < lenA || ib < lenB ){
    if ( ia == lenA ) {
      LC[ic] = LB[ib];
      RC[ic] = RB[ib];
      ++ib, ++ic;
    }
    else if ( ib == lenB  ) {
      LC[ic] = LA[ia];
      RC[ic] = RA[ia];
      ++ia, ++ic;
    }
    else if ( LA[ia] < LB[ib] ) {
      LC[ic] = LA[ia];
      RC[ic] = RA[ia];
      ++ia, ++ic;
    }
    else if ( LA[ia] > LB[ib] ) {
      LC[ic] = LB[ib];
      RC[ic] = RB[ib];
      ++ib, ++ic;
    }
    else {
      LC[ic] = LA[ia];
      RC[ic] = ( RA[ia] > RB[ib] ) ? ( RA[ia] ) : (RB[ib] ); //< maksimum z istotnosci
      ++ia, ++ib, ++ic;
    }
  }
  argC->size = ic;
}

/// QuickSort przekopiowany z wikipedii
void sortArticles ( int *articles, float *relevance, int from, int to ){

   int i,j;
   float x = relevance[from];
   i = from;
   j = to;
   do
   {
      while (relevance[i] > x) ++i;
      while (relevance[j] < x) --j;
      if (i <= j)
      {
         float tmpF = relevance[i];
         int tmpI = articles[i];
         articles[i]  = articles[j];
         relevance[i] = relevance[j];
         articles[j]  = tmpI;
         relevance[j] = tmpF;
         ++i; --j;
      }
   } while(i < j);
   if (from < j) sortArticles(articles, relevance, from, j);
   if (to > i) sortArticles(articles, relevance, i, to);

}



#include <Python.h>
list iter1, iter2, iter3;


static int PythonReadArgs ( PyObject* args ){
    if (!PyArg_ParseTuple(args, "(iiii)(iiii)",
        &iter1.arts,
        &iter1.size,
        &iter1.relevance,
        &iter1.fromIndex,
        &iter2.arts,
        &iter2.size,
        &iter2.relevance,
        &iter2.fromIndex
         )
        ) return 0;
    
    
    iter3.arts = malloc ( maxSize * sizeof ( int )  );
    iter3.relevance = malloc ( maxSize * sizeof ( float )  );
    assert ( iter3.arts != NULL && "Nie mozna przydzielic pamieci" );
    assert ( iter3.relevance != NULL && "Nie mozna przydzielic pamieci" );
    return 1;
}

static PyObject* PythonCreateList ( int *from, int len){
    assert (len < maxSize);
PyObject* list = PyList_New( len );
    int i;
    for( i = 0 ; i < len; i++){
        PyList_SetItem(list, i, PyLong_FromLong( from[i] ) );
    }
    return list;
}

static PyObject* returnPython () {
    if ( !iter1.fromIndex ){
        free ( iter1.arts );
        free ( iter1.relevance );
    }
    if ( !iter2.fromIndex ){
        free ( iter2.arts );
        free ( iter2.relevance );
    }
    return Py_BuildValue("(iiii)", iter3.arts, iter3.size, iter3.relevance, 0 );
}


static PyObject* andArticles (PyObject* self, PyObject* args){
    if ( !PythonReadArgs ( args ) ) return NULL;
    _andArticles( &iter1, &iter2, &iter3 );
    return returnPython ();
}

static PyObject* orArticles (PyObject* self, PyObject* args){
    if ( !PythonReadArgs ( args ) ) return NULL;
    _orArticles( &iter1, &iter2, &iter3 );
    return returnPython ();
}

static PyObject* getFloats2(PyObject* self, PyObject* args)
{
    int l1, len, l2, freeable;
    if (!PyArg_ParseTuple(args, "(iiii)", &l1, &len, &l2, &freeable))
        return NULL;

    float* list = (float *) l2;
    
    PyObject* listaPythonowa = PyList_New( len );

    int i;
    for( i = 0 ; i < len; i++)
    {
        PyList_SetItem(listaPythonowa, (Py_ssize_t) i, PyFloat_FromDouble( (double) list[i] ) );
    }
    return listaPythonowa ;
    
    
} 

static PyObject* getArticlesFloats ( PyObject* self, PyObject* args ){
    int *tab;
    float *rel;
    int siz;
    int fromIndex;
    if (!PyArg_ParseTuple(args, "(iiii)",
        &tab,
        &siz,
        &rel,
        &fromIndex
         )) return NULL;
    
    if ( fromIndex ) {
        int *tab2 = malloc ( siz * sizeof ( int ) );
        float *rel2 = malloc ( siz * sizeof ( float ) );
        memcpy ( tab2, tab, siz * sizeof ( int ) );
        memcpy ( rel2, rel, siz * sizeof ( int ) );
        tab = tab2;
        rel = rel2;
    }
    
    sortArticles ( tab, rel, 0, siz - 1);
    
    PyObject *ret = PythonCreateList ( tab, siz );
    free ( tab );
    free ( rel );
    return ret;
}


static PyObject* getTables(PyObject* self, PyObject* args)
{
    PyObject * o1;
    PyObject * o2;
    PyObject * o3;    
    int foo;
    if (!PyArg_ParseTuple(args, "OOOi(ffffffffffff)", &o1, &o2, &o3, &foo, &c1, &c2, &c3, &c4, &c5, &c6, &c7,&c8, &c9, &c10, &c11, &c12))
        return NULL;
    
    score = (int *) foo;
    int len1 = (int) PyList_Size( o1 );
    int len2 = (int) PyList_Size( o2 );
    int len3 = (int) PyList_Size( o3 );
    pageRankofArticles = malloc( len1 * sizeof(float ));
    lenOfArticles = malloc( len2 * sizeof(int ));        
    inne = malloc( len3 * sizeof(int ) );
    
    int i;
    for( i = 1 ; i < len1 ; i++)
    {
        pageRankofArticles[i] = PyFloat_AsDouble( PyList_GET_ITEM(o1, i) );
    }
    
    for( i = 1 ; i < len2 ; i++)
    {
        lenOfArticles[i] = PyLong_AsLong( PyList_GET_ITEM(o2, i) );
    }

    for( i = 1 ; i < len3 ; i++)
    {
        inne[i] = PyLong_AsLong( PyList_GET_ITEM(o3, i) );
    }

   return Py_BuildValue("i",  0 ) ;     
}

inline float tajnaFunkcja( float dp, float pr, int len, int l, float pr2, int i)
{
    if( l < 40 ){
    
        if( pr > 0 && pr2 > 0){
            return ( c1 * log( len ) + c2 * log( pr ) + dp + c3 * log(pr2) - c4 * i );
        }
        else{
            return ( c1 * log( len ) + dp  - c4 * i);
        }
    }
    else if( l < 120 ){
        
        if( pr > 0 && pr2 > 0){
            return ( c5 * log( len ) + c6 * log( pr  ) + dp + c7 * log(pr2)  - c8 * i  )  ;
        }
        else{
            return ( c5 * log( len ) + dp  - c7 * i);
        }
        
    }
    else{
    
        if( pr > 0 && pr2 > 0){
            return ( c9 * log( len ) + c10 * log( pr  ) + dp  + c11 * log(pr2)  - c12 * i  ) ;
        }
        else{
            return ( c9 * log( len ) + dp - c12 * i );
        }

    }
    
    
}

static PyObject* change(PyObject* self, PyObject* args)
{
    int l1, len, l2, turns, freeable;
    
    if (!PyArg_ParseTuple(args, "i(iiii)", &turns,&l1, &len, &l2, &freeable))
        return NULL;
    int* listOfArticles = (int *) l1;
    float* listOfFloats = (float *) l2;

    int i;
    for( i = 0 ; i < len; i++)
    {
        listOfFloats[i] = tajnaFunkcja(listOfFloats[i], pageRankofArticles[ listOfArticles[i] ], lenOfArticles[ listOfArticles[i] ], len,
          ( (float) score[ listOfArticles[i] ] ) / ( (float) turns), inne[  listOfArticles[i] ] ) ;
    }
    
    return Py_BuildValue("i",  0 );
}

static PyObject* setConstans(PyObject* self, PyObject* args)
{
    if (!PyArg_ParseTuple(args, "(ffffffffffff)", &c1, &c2, &c3, &c4, &c5, &c6, &c7,&c8, &c9, &c10, &c11, &c12) )
        return NULL;    
    
    return Py_BuildValue("i",  0 );
}

static PyObject* getArticles2(PyObject* self, PyObject* args)
{
    int l1, len, l2, freeable;
    if (!PyArg_ParseTuple(args, "(iiii)", &l1, &len, &l2, &freeable))
        return NULL;
    int* list = (int *) l1;
    
    PyObject* listaPythonowa = PyList_New( len );
    int i;
    for( i = 0 ; i < len; i++)
    {
        PyList_SetItem(listaPythonowa, (Py_ssize_t) i, PyLong_FromLong( list[i] ) );
    }
    
    return listaPythonowa ;
}


static PyMethodDef HelloMethods[] =
{
     {"andArticles", andArticles, METH_VARARGS, " "},
     {"change", change, METH_VARARGS, " "},
     {"orArticles", orArticles, METH_VARARGS, " "},
     {"get", getArticlesFloats, METH_VARARGS, " "},
     {"getArticles", getArticles2, METH_VARARGS, " "},
     {"getFloats", getFloats2, METH_VARARGS, " "},     
     {"getTables", getTables, METH_VARARGS, " "},         
     {"setConstans", setConstans, METH_VARARGS, " "},              
     {NULL, NULL, 0, NULL}
};
 
PyMODINIT_FUNC initrapidUtils(void)
{   
     (void) Py_InitModule("rapidUtils", HelloMethods);
}
